package org.opens.apache.io;

import org.apache.commons.io.IOUtils;
import org.junit.Test;

import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;

public class ReadInputStream {

    private File file = new File("C:\\Users\\张玉明\\Desktop\\test\\read.txt");

    /**
     * 功能:
     *      使用常规的方式读取输入流.
     */
    @Test
    public void testInputStreamToString() throws IOException {
        InputStream in = new URL( "http://commons.apache.org" ).openStream();
        InputStreamReader inR = null;
        BufferedReader buf = null;
        try {
            inR = new InputStreamReader( in );
            buf = new BufferedReader( inR );
            String line;
            StringBuilder stringBuffer = new StringBuilder();
            while ( ( line = buf.readLine() ) != null ) {
                stringBuffer.append(line);
            }
            System.out.println(stringBuffer.toString());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(buf != null) {
                buf.close();
            }
            if(inR != null) {
                inR.close();
            }
            in.close();
        }
    }

    /**
     * 功能:
     *      可以使用IOUtils.closeQuietly(in)方法来快速的关闭输入流, 但是在2.6+的commons.io中发现, 这个方法过时了, 原因在下面说明.
     * 说明:
     *      1. IOUtils.closeQuietly(in)方法过时的说明:
     *          - 之所以弃用这个方法, 是因为在jdk1.7+之后, 引入了一种try-with-resource的机制.
     *              1. 该机制就像`代码重排序`机制一样, 作用与.java文件编译为.class文件的过程.
     *              2. try-with-resource的机制: 之前定义在try catch外部的流移到了try后边的括号里，然后finally里就不再需要手动关闭了，
     *              它会自行处理。
     *                  - 编译后的程序会自动关闭try语句块中的InputStream.
     *                  - 但是需要注意的是: 在try-with-resource机制中, try语句块中的输入流默认是隐式final的, 不可以重新赋值, 无法再改变
     *                  其内存引用地址.
     *      2. 这种方式有好处也有弊端:
     *          - 好处: 这个类可以节省很多时间。并且你可以依赖于经过全面测试的代码
     *          - 弊端: 对于这类的代码,灵活性和速度是最重要的。然而你也应该明白这么做的限制。使用以上的方式读取一个1GB的文件将会试图创造一个1GB
     *          字符串对象. (读取大流会非常浪费内存, 因为字符串存储在堆中).
     *
     */
    @Test
    public void test2() throws IOException {
        InputStream in = new URL( "http://commons.apache.org" ).openStream();
        try {
            System.out.println( IOUtils.toString( in ) );
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            IOUtils.closeQuietly(in);
        }
    }

    /**
     * 功能:
     *      验证try-with-resource机制中, try语句块中的输入流默认是隐式final的, 不可以重新赋值, 无法再改变其内存引用地址.
     * 输出:
     *      java.io.FileNotFoundException: a.txt (系统找不到指定的文件。)
     * 结论:
     *      1. 可以重新赋值, 还是老老实实使用IOUtils.closeQuietly(in);进行关闭吧.
     */
    @Test
    public void test3() {
        try {
            InputStream in = new URL( "http://commons.apache.org" ).openStream();
            in = new FileInputStream(new File("a.txt"));
            System.out.println( IOUtils.toString( in ) );
        } catch (IOException e) {
            e.printStackTrace();
        } finally {

        }
    }

    /**
     * 功能:
     *      将文件读成byte[].
     * 输出:
     *      49 50 51 52 53 54 43 13 10
     *      49 50 51 52 53 54 45 13 10
     *      49 50 51 52 53 54 61 13 10
     *      49 50 51 52 53 54 33 13 10
     *      49 50 51 52 53 54 126
     *      43
     * 说明:
     *      1. 这里的数字请参考ASCII进行查看.
     *          - 13代表CR回车;
     *          - 10代表LF换行.
     *      2. 如果输出的话, 是看不到13和10对应的字符的,
     *          - 这也是为什么文件中可以看到的只有35个字符, 但是byte[]的长度为43的原因.
     *      3. IOUtils.toByteArray方法其实实现很简单, 就是将输入流的内容copy到输出流, 然后使用输出流的toByteArray方法将输出流转换为byte[].
     */
    @Test
    public void test4() throws IOException {
        FileInputStream fileInputStream = new FileInputStream(file);
        byte[] bytes = IOUtils.toByteArray(fileInputStream);
        int i = 0;
        for (byte aByte : bytes) {
            System.out.print(aByte + " ");
            if(i == 8) {
                i = 0;
                System.out.println();
                continue;
            }
            i++;
        }
        System.out.println();
        System.out.println(bytes.length);
        IOUtils.closeQuietly(fileInputStream);
    }

    /**
     * 功能:
     *      使用IOUtils.copy方法将输入流的内容复制到输出流中.
     * 输出:
     *      43
     * 说明:
     *      1. IOUtils.copy方法会将复制的长度返回.
     *      2. 可以发现, 这个工具极大的简化了IO操作.
     */
    @Test
    public void test5() throws IOException {
        File fileCopy = new File("C:\\Users\\张玉明\\Desktop\\test\\read_copy.txt");
        FileInputStream inputStream = new FileInputStream(file);
        FileOutputStream fileOutputStream = new FileOutputStream(fileCopy);

        int copy = IOUtils.copy(inputStream, fileOutputStream);
        System.out.println(copy);
        IOUtils.closeQuietly(fileOutputStream);
        IOUtils.closeQuietly(inputStream);
    }

}
